package mornd.test3;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 自己添加 map 集合实现监听器适配调用
 */

//@Component("applicationEventMulticaster") // beanName 是固定的，用于覆盖掉 spring 默认的事件发布器
class MyEventMulticaster2 extends AbstractEventMulticaster {

    @Autowired
    private ThreadPoolTaskExecutor executor;

    /**
     * 保存所有自己添加的监听器
     *
     *  key 是监听器对象
     *  value 是该监听器可以处理的事件类型
     */
    private final Map<ApplicationListener, Class<? extends ApplicationEvent>> listeners = new LinkedHashMap<>();

    /**
     * 收集监听器
     * @param listener 监听器 bean 对象
     */
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        // 获取所有接口（没有泛型的也会获取，比如 Serializable 接口）
        for (Type genericInterface : listener.getClass().getGenericInterfaces()) {
            try {
                // 类型转换
                ParameterizedType type = (ParameterizedType) genericInterface;
                // 获取接口上的所有泛型
                Type[] actualTypeArguments = type.getActualTypeArguments();
                if(actualTypeArguments.length > 0) {
                    Class actualTypeArgument = (Class) actualTypeArguments[0];
                    // 接口泛型是否是 ApplicationEvent 类型或是其子类型
                    if(ApplicationEvent.class.isAssignableFrom(actualTypeArgument)) {
                        listeners.put(listener, actualTypeArgument);
                    }
                }
            } catch (ClassCastException e) {
                // 忽略
            }
        }
    }

    /**
     * 收集监听器
     * @param listenerBeanName 实现监听器接口的 beanName
     */
    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
    }

    /**
     * 真正发事件方法
     * @param event 这里 spring 也会传入其它的事件 比如：ContextRefreshedEvent，所以需要做校验，否则类型转换错误
     * @param eventType
     */
    @Override
    public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
        for (Map.Entry<ApplicationListener, Class<? extends ApplicationEvent>> entry : listeners.entrySet()) {
            // spring 传进来的事件类型是否是自定义监听器所监听的事件类型或是其子类
            if (entry.getValue().isAssignableFrom(event.getClass())) {
                // 异步执行
                executor.submit(() -> entry.getKey().onApplicationEvent(event));
            }
        }
    }
}
